package com.example.test.主方法运行测试;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import javax.swing.JOptionPane;

/**
 * 金额随机分配算法
 * @author kwf
 * @since 2018-2-9 11:03:59
 */
public class Main {
    //需要设置的参数
    private static final double MIN_MONEY = 0.1; //用户获得的最小金额
    private static final double MAX_MONEY = 10; //用户获得的最大金额（可能存在误差。主要用于计算倍数，值为0则使用默认倍数）
    private static final double TOTAL_MONEY = 100;//总金额
    private static final int TOTAL_COUNT = 50;//总份数
    private static final boolean IS_UPSET = true;//结果是否需要打乱
    private static double times = 2;//倍数（用户获得的最大金额=当前平均值*倍数，当前平均值=剩余金额/剩余份数）（若最大金额不为0则会被重新赋值）

    private static double leftMoney = TOTAL_MONEY; //剩余金额
    private static int leftCount = TOTAL_COUNT;//剩余份数

    private static int runCount = 0;//运行次数
    private static int minCount = 0;//算法中获得的最小金额的个数
    private static double maxValue, minValue; //算法中获得的最大值和最小值

    public static void main(String[] args) {
        System.out.println("倍数为" + setTimes());
        if(!isRight(TOTAL_MONEY, TOTAL_COUNT)) { //如果设置金额和份数不合法则报错
            JOptionPane.showMessageDialog(null, "平均值为小于最小金额", "出错啦", JOptionPane.ERROR_MESSAGE);
            return;
        }

        List<Double> list = new ArrayList<>(); //用于存储金额列表
        double total = 0;//用于存储金额列表的总值
        int size = TOTAL_COUNT;
        for(int i = 0; i < size; size--) {
            double money = getRandomMoney();
            list.add(money);
            total += money;
            if(Double.doubleToLongBits(money) == Double.doubleToLongBits(MIN_MONEY)) minCount ++;
        }
        if(IS_UPSET) {
            System.out.println("打乱前：" + list); //打印打乱前的金额列表
            Collections.shuffle(list);//打乱列表
            System.out.println("打乱后：" + list); //打印打乱后的金额列表
        } else{
            System.out.println(list); //打印金额列表
        }
        System.out.println("数组总额为" + Math.round(total) + "=>" + (Math.round(total) == Math.round(TOTAL_MONEY) ? "合法" : "不合法"));
        System.out.println("随机算法运行了" + runCount + "次"); //打印金额列表


        maxValue = minValue = list.get(0);
        for(double value:list) {
            maxValue = value > maxValue ? value : maxValue;
            minValue = value < minValue ? value : minValue;
        }
        System.out.println("最大值为" + maxValue + "，最小值为" + minValue);
        System.out.println("最小金额的个数为" + minCount);
    }

    /**
     * 随机算法
     * @return 随机金额
     */
    public static double getRandomMoney() {
        runCount ++;
        if (leftCount == 1) {
            return (double) Math.round(leftMoney * 100) /100;
        }
        Random r = new Random();
        double max = leftMoney / leftCount * times;
        double money = r.nextDouble() * max;
        money = money <= MIN_MONEY ? MIN_MONEY : money;
        money = Math.floor(money * 100) / 100;
        if(isRight(leftMoney - money, leftCount - 1)) {
            leftMoney -= money;
            leftCount--;
            return money;
        } else {//如果不合法则递归调用随机算法，直到合法
            return getRandomMoney();
        }
    }

    /**
     * 判断金额和份数是否合法，平均值小于最小金额则视为不合法
     * @param money 金额
     * @param count 份数
     * @return 合法性
     */
    private static boolean isRight(double money, int count) {
        return money / count >= MIN_MONEY;
    }

    /**
     * 设置倍数（仅当设置了最大金额才有效，否则为默认倍数）
     * @return 倍数
     */
    private static double setTimes() {
        if(MAX_MONEY != 0) {
            times = MAX_MONEY / (TOTAL_MONEY / TOTAL_COUNT);
        }
        return times;
    }
}